import sys

import numpy as np
import soundcard as sc
import soundfile as sf
import threading
import time
import os
from nicegui import ui, Client
from typing import Optional, Dict, Any
import asyncio

from pydub import AudioSegment

# 配置参数
Config = {
    'sample_rate': 192000,
    'bit_depth': 'PCM_24',
    'normalize_volume': True,
    'compression': False,
    'target_max_amplitude': 0.9,
    'output_path': r'E:\DOCUMENTS\音乐\音乐',
    'output_filename': 'Recording',
    'output_format': 'wav'  # 支持 'wav' 或 'flac'
}

BIT_DEPTH_OPTIONS = {
    '16bit': 'PCM_16',
    '24bit': 'PCM_24',
    '32bit': 'PCM_32',
    'f32': 'FLOAT',
    'f64': 'DOUBLE',
    'dsd': 'DSD',
}


class AudioRecorder:
    def __init__(self):
        self.is_recording = False
        self.is_paused = False
        self.recording_data = []
        self.record_thread: Optional[threading.Thread] = None
        self.current_device = None
        self.lock = threading.Lock()
        self.stop_recording_flag = threading.Event()

        # 实时电平和时长相关属性
        self.current_level = 0.0
        self.level_lock = threading.Lock()
        self.start_time = None
        self.duration_lock = threading.Lock()

        # 新增平滑系数和历史电平
        self.smoothing_factor = 0.9  # 平滑系数（0.1~0.9）
        self.prev_db_level = -60.0  # 初始电平

    def start_recording(self):
        if not self.current_device:
            return

        with self.lock:
            if self.is_recording:
                return

            self.is_recording = True
            self.is_paused = False
            self.recording_data = []
            self.stop_recording_flag.clear()
            self.record_thread = threading.Thread(target=self._record_loop)
            self.record_thread.start()

            # 初始化时长
            with self.duration_lock:
                self.start_time = time.time()
            # 重置电平
            with self.level_lock:
                self.current_level = 0.0

    def pause_recording(self):
        with self.lock:
            if self.is_recording:
                self.is_paused = not self.is_paused

    def stop_recording(self):
        with self.lock:
            self.is_recording = False
            self.is_paused = False
            self.stop_recording_flag.set()
            self.record_thread = None

        # 重置电平和时长
        with self.level_lock:
            self.current_level = 0.0
        with self.duration_lock:
            self.start_time = None

    def _record_loop(self):
        while self.is_recording and not self.stop_recording_flag.is_set():
            try:
                with self.current_device.recorder(Config['sample_rate']) as recorder:
                    while self.is_recording and not self.stop_recording_flag.is_set():
                        if self.is_paused:
                            time.sleep(0.1)
                            continue
                        data = recorder.record(numframes=1024)
                        with self.lock:
                            self.recording_data.append(data)

                        # 计算 RMS
                        if data.ndim == 2:  # 立体声（多通道）
                            data = data.mean(axis=1)  # 取左右声道平均值
                        rms = np.sqrt(np.mean(data ** 2))
                        if rms < 1e-10:
                            db_level = -100
                        else:
                            db_level = 20 * np.log10(rms)

                        # 指数平滑处理
                        self.prev_db_level = (
                                db_level * (1 - self.smoothing_factor) +
                                self.prev_db_level * self.smoothing_factor
                        )

                        # 限制分贝范围
                        db_level = max(min(db_level, 0), -60)  # 映射到 -60~0 dB
                        progress_value = (db_level + 60) / 60  # 映射到 0~1

                        with self.level_lock:
                            self.current_level = progress_value
            except Exception as e:
                print(f"录音错误: {e}")
                break

    async def save_recording(self):
        if not self.recording_data:
            return

        try:
            full_path = os.path.join(
                Config['output_path'],
                f"{Config['output_filename']}.{Config['output_format']}"
            )

            data = np.concatenate(self.recording_data)

            if Config['normalize_volume']:
                max_amp = np.max(np.abs(data))
                if max_amp > 0:
                    scale = Config['target_max_amplitude'] / max_amp
                    data = data * scale
            sf.write(full_path, data, Config['sample_rate'],
                     subtype=Config['bit_depth'], format=Config['output_format'])
            if Config['compression']:
                self._apply_compression(full_path)
            ui.notify(f"保存成功: {full_path}")
        except Exception as e:
            ui.notify(f"保存失败: {str(e)}", type='negative')

    def _apply_compression(self, file_path):
        try:
            audio = AudioSegment.from_file(file_path, format=Config['output_format'])
            compressed = audio.compress_dynamic_range(
                threshold=-18.0,
                attack=10,
                release=200,
                ratio=2
            )
            compressed.export(file_path, format=Config['output_format'])
        except Exception as e:
            ui.notify(f"压缩失败: {str(e)}", type='warning')

def update_bit_depth_options():
    return {k: f"{k} ({v})" for k, v in BIT_DEPTH_OPTIONS.items()}


def list_devices():
    return [device.name for device in sc.all_microphones(include_loopback=True)]


def select_device(device_name):
    for device in sc.all_microphones(include_loopback=True):
        if device.name == device_name:
            return device
    return None


async def start_recording_handler():
    stop_btn.enable()
    pause_btn.enable()
    recorder.start_recording()
    start_btn.disable()


async def pause_recording_handler():
    recorder.pause_recording()
    pause_btn.set_text('继续' if recorder.is_paused else '暂停')


async def stop_recording_handler():
    print("停止按钮已点击")
    recorder.stop_recording()
    await recorder.save_recording()
    start_btn.enable()
    stop_btn.disable()
    pause_btn.disable()
    pause_btn.set_text('暂停')


# 创建UI组件
recorder = AudioRecorder()

with ui.column().classes('max-w-lg mx-auto p-4'):
    # 设备选择
    device_dropdown = ui.select(
        options=list_devices(),
        label='选择录音设备',
        on_change=lambda e: setattr(recorder, 'current_device', select_device(e.value))
    )

    # 参数设置
    with ui.expansion('录制参数').classes('w-full'):
        sample_rate_options = {
            '44.1k': 44100,
            '48k': 48000,
            '96k': 96000,
            '192k': 192000,
            '384k': 384000,
            '768k': 768000,
        }
        sample_rate_input = ui.select(
            options=sample_rate_options,
            label='采样率',
            value='192k'
        )
        bit_depth_input = ui.select(
            options=update_bit_depth_options(),
            label='位深度',
            value='24bit'
        )
        normalize_checkbox = ui.checkbox('自动归一化音量', value=Config['normalize_volume'])
        compression_checkbox = ui.checkbox('启用动态压缩', value=Config['compression'])
        target_amp_input = ui.number(label='目标最大振幅 (0-1)', value=Config['target_max_amplitude'], min=0, max=1,
                                     step=0.1)

    # 输出设置
    with ui.expansion('输出设置').classes('w-full'):
        output_path_input = ui.input(label='保存路径', value=Config['output_path'])
        filename_input = ui.input(label='文件名', value=Config['output_filename'])
        format_dropdown = ui.select(options=['wav', 'flac'], label='文件格式', value='wav')

    # 控制按钮
    with ui.row().classes('w-full justify-center space-x-2 p-4'):
        start_btn = ui.button('开始录制', on_click=start_recording_handler)
        pause_btn = ui.button('暂停', on_click=pause_recording_handler)
        pause_btn.disable()
        stop_btn = ui.button('停止', on_click=stop_recording_handler)
        stop_btn.disable()

    # 状态显示（修改部分）
    status_label = ui.label('就绪').classes('text-center text-lg')
    """level_progress = ui.linear_progress(value=0.0).props(
        'rounded color="green-500" transition="0s"'
    ).classes('w-full')"""
    # 初始化 SVG 画布
    vu_meter = ui.html('<svg id="vu_meter" width="200" height="30"></svg>')
    duration_label = ui.label('时长: 00:00').classes('text-center text-blue-500')


# 更新配置参数
def update_config():
    Config['sample_rate'] = sample_rate_options[sample_rate_input.value]
    Config['bit_depth'] = BIT_DEPTH_OPTIONS[bit_depth_input.value]
    Config['normalize_volume'] = normalize_checkbox.value
    Config['compression'] = compression_checkbox.value
    Config['target_max_amplitude'] = target_amp_input.value
    Config['output_path'] = output_path_input.value
    Config['output_filename'] = filename_input.value
    Config['output_format'] = format_dropdown.value


# 每次参数变化时更新配置
sample_rate_input.on_value_change(update_config)
bit_depth_input.on_value_change(update_config)
normalize_checkbox.on_value_change(update_config)
compression_checkbox.on_value_change(update_config)
target_amp_input.on_value_change(update_config)
output_path_input.on_value_change(update_config)
filename_input.on_value_change(update_config)
format_dropdown.on_value_change(update_config)


# 页面更新间隔
async def update_status():
    status = '录制中...' if recorder.is_recording else '就绪'
    if recorder.is_paused:
        status = '已暂停'
    status_label.set_text(status)

    # 实时电平更新（进度条）
    with recorder.level_lock:
        progress_value = recorder.current_level
    # 在电平更新逻辑中
    # progress_value = (db_level + 60) / 60  # 映射为 0~1 的值
    ui.run_javascript(
        f"""
        const svg = document.getElementById('vu_meter');
        svg.innerHTML = '';
        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        rect.setAttribute('width', {progress_value * 200});
        rect.setAttribute('height', '30');
        rect.setAttribute('fill', 'red');
        svg.appendChild(rect);
        """,
        timeout=0.05
    )

    # 录制时长更新（保留原有逻辑）
    duration_text = '00:00'
    with recorder.duration_lock:
        if recorder.is_recording and recorder.start_time is not None:
            elapsed = time.time() - recorder.start_time
            mins = int(elapsed // 60)
            secs = int(elapsed % 60)
            duration_text = f'{mins:02d}:{secs:02d}'
    duration_label.set_text(f'时长: {duration_text}')


ui.timer(0.05, update_status)

ui.run(title='音频录制器', reconnect_timeout=30)
